package aupdg.logic.analysis.impl;

import aupdg.data.model.Edge;
import aupdg.data.model.Ldg;
import aupdg.data.model.Node;
import aupdg.data.model.impl.AuControlDependenceEdge;
import aupdg.data.model.impl.AuPredicateNode;
import aupdg.logic.analysis.InformationLeakAnalysis;
import aupdg.logic.analysis.SecurityLevel;
import java.util.*;

/**
 * Find implicit information leaks in the LDG.
 *
 * @author AA and CJ
 */
public class ImplicitILAnalysis implements InformationLeakAnalysis {

    private Ldg ldg;
    Stack<Node> path = new Stack<>();
    Set<Node> nodesOnPath = new HashSet<>();

    /**
     * Constructor. Uses internally an LDG object.
     *
     * @param ldg
     */
    public ImplicitILAnalysis(Ldg ldg) {
        this.ldg = ldg;
    }

    @Override
    public Map<Node, List<List<Node>>> detectInformationLeaks() {
        HashMap<Node, List<List<Node>>> infoLeaksMap = new HashMap<>();

        System.out.println("+++++++++++++++++Implicit information Leaks+++++++++++++++++++ ");
        for (int i = 0; i < this.ldg.getPredicateNodes().size(); i++) {
            //AuIfNode ifNode = (AuIfNode)this.ldg.getIfNodes().get(i);
            AuPredicateNode predNode = (AuPredicateNode) this.ldg.getPredicateNodes().get(i);

            List<List<Node>> nodesWithLeak = getNodesWithLeak(predNode);
            if (!nodesWithLeak.isEmpty()) {
                infoLeaksMap.put(predNode, nodesWithLeak);
            }
            System.out.println(predNode + " -> " + nodesWithLeak);
        }
        return infoLeaksMap;
    }

    /**
     * Retrieves all the predicate nodes with implicit information leaks.
     *
     * @param predNode
     * @return
     */
    public List<List<Node>> getNodesWithLeak(Node predNode) {
        List<Node> reachableNodes = new ArrayList<>();
        List<List<Node>> nodesWithLeak = new ArrayList<>();

        Stack<Node> path = new Stack<>();
        Set<Node> nodesOnPath = new HashSet<>();
        List<Edge> adjEdges;

        if (predNode.getLevel() == SecurityLevel.LOW) {
            return nodesWithLeak;
        }
        path.push(predNode);
        nodesOnPath.add(predNode);

        while (!nodesOnPath.isEmpty()) {
            Node currentNode = path.pop();
            //path.push(currentNode);

            adjEdges = this.ldg.getAdjacentEdgesFromNode(currentNode);
            for (int i = 0; i < adjEdges.size(); i++) {
                if (!nodesOnPath.contains(adjEdges.get(i).getTarget())
                        && adjEdges.get(i) instanceof AuControlDependenceEdge) {
                    nodesOnPath.add(adjEdges.get(i).getTarget());
                    reachableNodes.add(adjEdges.get(i).getTarget());
                    if (adjEdges.get(i).getTarget().getLevel() == SecurityLevel.LOW) {
                        List<Node> leakFromNodeToNode = new ArrayList<>();
                        leakFromNodeToNode.add(predNode);
                        leakFromNodeToNode.add(adjEdges.get(i).getTarget());
                        nodesWithLeak.add(leakFromNodeToNode);
                    }
                    path.push(adjEdges.get(i).getTarget());
                }
            }
            nodesOnPath.remove(currentNode);
        }
        return nodesWithLeak;
    }
}
